package top.milkbox.core.handler;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.ui.Model;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.annotation.HandlerMethodValidationException;
import top.milkbox.common.enums.CommonStatusCodeEnum;
import top.milkbox.common.exceprion.CommonServiceException;
import top.milkbox.common.pojo.CommonResult;
import top.milkbox.sys.modular.permission.service.SysPermissionService;
import top.milkbox.sys.modular.permission.vo.SysPermissionVo;

import java.util.Arrays;

/**
 * 全局controller增强器
 * 创建时间: 2024-01-10 下午 2:29
 *
 * @author milkbox
 */
@Slf4j
@ControllerAdvice
public class GlobalControllerAdvice {

    @Autowired
    private SysPermissionService sysPermissionService;

    /**
     * 把值绑定到Model中，使全局@RequestMapping可以获取到该值
     */
    @ModelAttribute
    public void addAttributes(Model model) {
//        log.info(model.toString());
    }

    /**
     * 应用到所有@RequestMapping注解方法，在其执行之前初始化数据绑定器
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
    }

    /**
     * 全局异常捕捉，在此处进行处理<br />
     * 打印日志堆栈的方式（Exception e）：<br />
     * <ul>
     *     <li>直接打印到控制台：<code>e.printStackTrace()</code></li>
     *     <li>通过日志方式打印：<code>log.error("其他字符串信息", e);</code></li>
     *     <li>获取堆栈数组：<code>e.getStackTrace()</code></li>
     * </ul>
     *
     * @param exception 捕获到的异常
     * @return 通用响应对象
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public CommonResult<Object> errorHandler(Exception exception) {

        // 服务层业务逻辑异常
        if (exception instanceof CommonServiceException commonServiceException) {
            return CommonResult.create()
                    .withCode(CommonStatusCodeEnum.ERROR506.getCode())
                    .withMessage(commonServiceException.getMessage());
        }

        // 用户未登录异常处理
        // 可以通过notLoginException的type属性来区分未登录的类型
        // 具体信息请查看cn.dev33.satoken.exception.NotLoginException
        else if (exception instanceof NotLoginException notLoginException) {
            notLoginException.setStackTrace(new StackTraceElement[0]); // 删除堆栈信息，因为它太长了
            return new CommonResult<>(CommonStatusCodeEnum.ERROR401.getCode(),
                    notLoginException.getMessage(), notLoginException);
        }

        // 无sa-token权限异常
        else if (exception instanceof NotPermissionException notPermissionException) {
            String permissionCode = notPermissionException.getPermission();
            // 根据权限码，从数据库中查询出这一条权限，用于拼接错误信息
            SysPermissionVo permissionVo = sysPermissionService.getByValue(permissionCode);
            StringBuilder errorMessage = new StringBuilder("无此权限：");
            if (ObjectUtil.isNotEmpty(permissionVo)) {
                errorMessage.append(permissionVo.getName());
            }
            return CommonResult.create()
                    .withCode(CommonStatusCodeEnum.ERROR403.getCode())
                    .withMessage(errorMessage.append("（").append(permissionCode).append("）").toString());
        }

        // 后端校验不通过异常
        else if (exception instanceof MethodArgumentNotValidException errorResponse) {
            StringBuilder errorMessage = new StringBuilder();
            errorResponse.getAllErrors()
                    .forEach(objectError -> errorMessage.append(objectError.getDefaultMessage()).append(StrUtil.COMMA));

            return new CommonResult<>(CommonStatusCodeEnum.ERROR415.getCode(),
                    CommonStatusCodeEnum.ERROR415.getMessage(),
                    StrUtil.removeSuffix(errorMessage.toString(), StrUtil.COMMA));
        }

        // 在controller接口方法参数上添加的校验注解，在此处单独报错
        else if (exception instanceof HandlerMethodValidationException errorResponse) {
            Object[] detailMessageArguments = errorResponse.getDetailMessageArguments();
            if (detailMessageArguments != null && detailMessageArguments.length != 0) {
                StringBuilder errorMessage = new StringBuilder();
                Arrays.stream(detailMessageArguments)
                        .forEach(objectError -> errorMessage.append(objectError.toString()).append(StrUtil.COMMA));
                return new CommonResult<>(CommonStatusCodeEnum.ERROR415.getCode(),
                        CommonStatusCodeEnum.ERROR415.getMessage(),
                        StrUtil.removeSuffix(errorMessage.toString(), StrUtil.COMMA));
            }
            log.warn("校验报错，但是校验选项为空");
        }

        // 请求参数的格式错误
        else if (exception instanceof HttpMessageNotReadableException httpMessageNotReadableException) {
            String errorMessage = "请求参数格式错误（" + httpMessageNotReadableException.getMessage() + "）";
            log.warn(errorMessage);
            return new CommonResult<>(CommonStatusCodeEnum.ERROR415.getCode(),
                    CommonStatusCodeEnum.ERROR415.getMessage(), errorMessage);
        }

        // 其他未处理异常，堆栈信息保存到日志中
        log.error("其他未处理异常：{}", exception.getMessage(), exception);
//        exception.printStackTrace(); // 系统控制台堆栈，可能不会存入日志
        return new CommonResult<>(CommonStatusCodeEnum.ERROR500.getCode(),
                exception.getMessage(), exception.getStackTrace());
    }
}
